package flexserverlib.messaging.services.messaging.adapters;

import flex.messaging.Destination;
import flex.messaging.MessageDestination;
import flex.messaging.config.ConfigMap;
import flex.messaging.config.ConfigurationException;
import flex.messaging.log.LogCategories;
import flex.messaging.messages.Message;
import flex.messaging.services.messaging.adapters.MessagingAdapter;
import flexserverlib.management.runtime.messaging.services.messaging.adapters.MailAdapterControl;
import flexserverlib.messaging.services.messaging.adapters.support.MailMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.mail.javamail.MimeMessagePreparator;

import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.util.Properties;

/**
 * Created by IntelliJ IDEA.
 * User: andrewpowell
 * Date: Aug 21, 2008
 * Time: 12:45:19 PM
 * To change this template use File | Settings | File Templates.
 *
 * @author Andrew Powell
 * @author Chris Scott
 */
public class MailAdapter extends MessagingAdapter implements MailConfigConstants {

    static final String LOG_CATEGORY = LogCategories.SERVICE_MESSAGE;

    private static final String DURABLE_SUBSCRIBER_NAME_PREFIX = "FlexClient_";

    private MailAdapterControl controller;

    private String serverName;
    private int serverPort;
    private String serverUser = null;
    private String serverPassword = null;
    private String messageType;
    private String defaultSender;
    private String defaultSubject;
    private String defaultRecipient;

    //--------------------------------------------------------------------------
    //
    // Spring MailSender components. I will probably move this into an abstract class
    //
    //--------------------------------------------------------------------------
    // private JavaMailSenderImpl mailSender;

    private JavaMailSenderImpl mailSender;
    private Session session;

    /**
     * Handles the message being sent from Flex to the <code>MailAdapter</code>
     *
     * @param message
     * @return
     */
    public Object invoke(Message message) {
        MailMessage flexMessage = (MailMessage) message.getBody();

        if (flexMessage != null)
            if (messageType == SIMPLE_MESSAGE) {
                org.springframework.mail.SimpleMailMessage msg = buildSimpleMessage(flexMessage);

                try {
                    mailSender.send(msg);
                } catch (MailException me) {
                    System.out.println(me.getMessage());
                }
            } else {
                // for a mime message, we will use a MimeMessagePreparator
                MimeMessagePreparator preparator = buildMimeMessage(flexMessage);

                try {
                    mailSender.send(preparator);
                } catch (MailException me) {
                    System.out.println(me.getMessage());
                }
            }

        return null;
    }

    //--------------------------------------------------------------------------
    //
    // Initialize, validate, start, and stop methods.
    //
    //--------------------------------------------------------------------------

    /**
     * Initializes the <code>MailAdapter</code> with the properties.
     *
     * @param properties Properties for the <code>MailAdapter</code>.
     */
    public void initialize(String id, ConfigMap properties) {
        super.initialize(id, properties);

        if (properties == null || properties.size() == 0)
            return;

        // mail specific properties
        mail(properties);
    }

    /**
     * Verifies that the <code>MailAdapter</code> is in valid state before
     * it is started.
     */
    protected void validate() {
        if (isValid())
            return;

        super.validate();

        if (this.serverName == null) {
            // Mail Server MUST be specified
            ConfigurationException ce = new ConfigurationException();
            ce.setMessage(MISSING_SERVER_NAME);
            throw ce;
        }
    }

    /**
     * Starts the adapter
     */
    public void start() {

        //TODO:  Initialize Spring Beans here
        super.start();

        /* MAIL SENDER */
        mailSender = new JavaMailSenderImpl();

        // create send session and mail sender
        Properties props = System.getProperties();
        props.put("mail.smtp.host", serverName);
        props.put("mail.smtp.port", serverPort);
        props.put("mail.smtp.auth", "true"); // need this line to have email server to relay!!
        // this 'starttls' is for sending mail via gmail...
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.transport.protocol", "smtp");
        session = Session.getInstance(props, new Auth()); // session with authentication !!!
        mailSender.setSession(session);
    }

    /**
     * Stops the adapter.
     */
    public void stop() {
        if (!isStarted())
            return;

        //TODO:  Tear down Spring Beans here, if needed

        super.stop();
    }

    /**
     * Handle mail specific configuration.
     */
    private void mail(ConfigMap properties) {
        ConfigMap mail = properties.getPropertyAsMap(MAIL, null);
        if (mail != null) {
            this.serverName = mail.getPropertyAsString(MAIL_SERVER, null);
            this.serverPort = mail.getPropertyAsInt(MAIL_SERVER_PORT, 25);
            this.serverUser = mail.getPropertyAsString(USERNAME, null);
            this.serverPassword = mail.getPropertyAsString(PASSWORD, null);
            this.messageType = mail.getPropertyAsString(MESSAGE_TYPE, SIMPLE_MESSAGE);
            this.defaultRecipient = mail.getPropertyAsString(DEFAULT_RECIPIENT, null);
            this.defaultSubject = mail.getPropertyAsString(DEFUALT_SUBJECT, null);
            this.defaultSender = mail.getPropertyAsString(DEFAULT_SENDER, null);
        }
    }

    /**
     * Builds a <code>SimpleMailMessage</code> from the <code>MailMessage</code> object
     *
     * @param flexMessage
     * @return
     */
    private SimpleMailMessage buildSimpleMessage(MailMessage flexMessage) {
        SimpleMailMessage msg = new SimpleMailMessage();

        msg.setTo(flexMessage.recipient != null ? flexMessage.recipient : this.defaultRecipient);
        msg.setFrom(flexMessage.sender != null ? flexMessage.sender : this.defaultSender);
        msg.setSubject(flexMessage.subject != null ? flexMessage.subject : this.defaultSubject);
        msg.setText(flexMessage.body);

        return msg;
    }

    /**
     * * Builds a <code>MimeMailMessage</code> from the <code>MailMessage</code> object
     *
     * @param flexMessage
     * @return
     */
    private MimeMessagePreparator buildMimeMessage(final MailMessage flexMessage) {

        MimeMessagePreparator preparator = new MimeMessagePreparator() {

            public void prepare(MimeMessage mimeMessage)
                    throws MessagingException, IOException {

                // use a message helper for html email
                MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

                if (flexMessage.recipient == null) {
                    helper.setTo(defaultRecipient);
                } else {
                    final String[] recipients = flexMessage.recipient.split(",");
                    for (int i = 0; i < recipients.length; i++)
                        helper.addTo(recipients[i]);
                }

                helper.setFrom(flexMessage.sender != null ? flexMessage.sender : defaultSender);
                helper.setSubject(flexMessage.subject != null ? flexMessage.subject : defaultSubject);
                helper.setText(flexMessage.body);

                // todo: add support for freemarker template rendering
            }
        };

        return preparator;
    }

    /**
     * Invoked automatically to allow the <code>ActionScriptAdapter</code> to setup its corresponding
     * MBean control.
     *
     * @param destination The <code>Destination</code> that manages this <code>ActionScriptAdapter</code>.
     */
    protected void setupAdapterControl(Destination destination) {
        controller = new MailAdapterControl(this, destination.getControl());
        controller.register();
        setControl(controller);
    }

    /**
     * Casts the <code>Destination</code> into <code>MessageDestination</code>
     * and calls super.setDestination
     *
     * @param destination
     */
    public void setDestination(Destination destination) {
        Destination dest = (MessageDestination) destination;
        super.setDestination(dest);
    }

    /**
     * inner class for mail session authentication
     */
    class Auth extends Authenticator {
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(serverUser, serverPassword);
        }
    }
}
